{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "8rOoC1NVjRcT"
   },
   "source": [
    "# Simple Time Series Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "bSEcFiPijRcb"
   },
   "source": [
    "This tutorial shows how a simple time series simulation is performed with the timeseries and control module in pandapower. A time series calculation requires the minimum following inputs:\n",
    "* pandapower net\n",
    "* the time series (in a pandas Dataframe for example)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "YquzqXkKjRcd"
   },
   "source": [
    "First we need some imports. Specific for this example are:\n",
    "* ConstControl -> \"constant\" controllers, which change the P and Q values of sgens and loads\n",
    "* DFData -> The Dataframe Datasource. This Dataframe holds the time series to be calculated\n",
    "* OutputWriter -> The output writer, which is required to write the outputs to the hard disk\n",
    "* run_timeseries -> the \"main\" time series function, which basically calls the controller functions (to update the P, Q of the ConstControllers) and runpp."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Dx9ZKvbfjRcg"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import tempfile\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from pandapower.timeseries import DFData, OutputWriter, run_timeseries\n",
    "from pandapower.control import ConstControl\n",
    "from pandapower.create import (\n",
    "    create_empty_network,\n",
    "    create_bus,\n",
    "    create_ext_grid,\n",
    "    create_line,\n",
    "    create_transformer,\n",
    "    create_sgen,\n",
    "    create_load,\n",
    "\n",
    ")\n",
    "from pandapower.run import set_user_pf_options\n",
    "\n",
    "rng = np.random.default_rng(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "NJj7q_srjRcs"
   },
   "source": [
    "First we look at the time series example function. It follows these steps:\n",
    "1. create a simple test net\n",
    "2. create the datasource (which contains the time series P values)\n",
    "3. create the controllers to update the P values of the load and the sgen\n",
    "4. define the output writer and desired variables to be saved\n",
    "5. call the main time series function to calculate the desired results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "E5i2ezcDjRcw"
   },
   "outputs": [],
   "source": [
    "def timeseries_example(output_dir):\n",
    "    # 1. create test net\n",
    "    net = simple_test_net()\n",
    "\n",
    "    # 2. create (random) data source\n",
    "    n_timesteps = 10\n",
    "    profiles, ds = create_data_source(n_timesteps)\n",
    "    # 3. create controllers (to control P values of the load and the sgen)\n",
    "    create_controllers(net, ds)\n",
    "\n",
    "    # time steps to be calculated. Could also be a list with non-consecutive time steps\n",
    "    time_steps = range(0, n_timesteps)\n",
    "\n",
    "    # 4. the output writer with the desired results to be stored to files.\n",
    "    ow = create_output_writer(net, time_steps, output_dir=output_dir)\n",
    "\n",
    "    # 5. the main time series function\n",
    "    run_timeseries(net, time_steps)\n",
    "    print(net.res_line.loading_percent)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xhzr8EE5jRc3"
   },
   "source": [
    "We start by creating a simple example pandapower net consisting of five buses, a transformer, three lines, a load and a sgen. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "x8hpgqhQjRc5"
   },
   "outputs": [],
   "source": [
    "def simple_test_net():\n",
    "    \"\"\"\n",
    "    simple net that looks like:\n",
    "\n",
    "    ext_grid b0---b1 trafo(110/20) b2----b3 load\n",
    "                                    |\n",
    "                                    |\n",
    "                                    b4 sgen\n",
    "    \"\"\"\n",
    "    net = create_empty_network()\n",
    "    set_user_pf_options(net, init_vm_pu = \"flat\", init_va_degree = \"dc\", calculate_voltage_angles=True)\n",
    "\n",
    "    b0 = create_bus(net, 110)\n",
    "    b1 = create_bus(net, 110)\n",
    "    b2 = create_bus(net, 20)\n",
    "    b3 = create_bus(net, 20)\n",
    "    b4 = create_bus(net, 20)\n",
    "\n",
    "    create_ext_grid(net, b0)\n",
    "    create_line(net, b0, b1, 10, \"149-AL1/24-ST1A 110.0\")\n",
    "    create_transformer(net, b1, b2, \"25 MVA 110/20 kV\", name='tr1')\n",
    "    create_line(net, b2, b3, 10, \"184-AL1/30-ST1A 20.0\")\n",
    "    create_line(net, b2, b4, 10, \"184-AL1/30-ST1A 20.0\")\n",
    "\n",
    "    create_load(net, b3, p_mw=15., q_mvar=10., name='load1')\n",
    "    create_sgen(net, b4, p_mw=20., q_mvar=0.15, name='sgen1')\n",
    "\n",
    "    return net"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "9CcyC2mgjRc_"
   },
   "source": [
    "The data source is a simple pandas DataFrame. It contains random values for the load and the sgen P values (\"profiles\"). Of course your time series values should be loaded from a file later on.\n",
    "Note that the profiles are identified by their column name (\"load1_p\", \"sgen1_p\"). You can choose here whatever you prefer.\n",
    "The DFData(profiles) converts the Dataframe to the required format for the controllers. Note that the controller"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "KVrcajmqjRdB"
   },
   "outputs": [],
   "source": [
    "def create_data_source(n_timesteps=24):\n",
    "    profiles = pd.DataFrame()\n",
    "    \n",
    "    profiles['load1_p'] = rng.random(n_timesteps) * 15.\n",
    "    profiles['sgen1_p'] = rng.random(n_timesteps) * 20.\n",
    "\n",
    "    ds = DFData(profiles)\n",
    "\n",
    "    return profiles, ds"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "_29FDhugjRdJ"
   },
   "source": [
    "create the controllers by telling the function which element_index belongs to which profile. In this case we map:\n",
    "* first load in dataframe (element_index=[0]) to the profile_name \"load1_p\"\n",
    "* first sgen in dataframe (element_index=[0]) to the profile_name \"sgen1_p\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "C7jNYZYhjRdN"
   },
   "outputs": [],
   "source": [
    "def create_controllers(net, ds):\n",
    "    ConstControl(net, element='load', variable='p_mw', element_index=[0],\n",
    "                 data_source=ds, profile_name=[\"load1_p\"])\n",
    "    ConstControl(net, element='sgen', variable='p_mw', element_index=[0],\n",
    "                 data_source=ds, profile_name=[\"sgen1_p\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "kDA6xHDWjRdT"
   },
   "source": [
    "create the output writer. Instead of saving the whole net (which takes a lot of time), we extract only pre defined outputs.\n",
    "In this case we:\n",
    "* save the results to \"../timeseries/tests/outputs\" \n",
    "* write the results to \".xls\" Excel files. (Possible are: .json, .p, .csv)\n",
    "* log the variables \"p_mw\" from \"res_load\", \"vm_pu\" from \"res_bus\" and two res_line values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "GlttGzEHjRdV"
   },
   "outputs": [],
   "source": [
    "def create_output_writer(net, time_steps, output_dir):\n",
    "    ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=\".xlsx\", log_variables=[])\n",
    "    # these variables are saved to the harddisk after / during the time series loop\n",
    "    ow.log_variable('res_load', 'p_mw')\n",
    "    ow.log_variable('res_bus', 'vm_pu')\n",
    "    ow.log_variable('res_line', 'loading_percent')\n",
    "    ow.log_variable('res_line', 'i_ka')\n",
    "    return ow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DAIvq_bUjRdb"
   },
   "source": [
    "Now lets execute the code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "wTlSALR0jRdd",
    "outputId": "959014ba-8db7-412b-d425-f2af199207f0"
   },
   "outputs": [],
   "source": [
    "output_dir = os.path.join(tempfile.gettempdir(), \"time_series_example\")\n",
    "print(\"Results can be found in your local temp folder: {}\".format(output_dir))\n",
    "if not os.path.exists(output_dir):\n",
    "    os.mkdir(output_dir)\n",
    "timeseries_example(output_dir)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "v8-ZYJFujRdk"
   },
   "source": [
    "If everything works you should have the desired results the output_folder, which is the temporary folder of your operating system (see print statement above)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "EzTC_NnRjRdn"
   },
   "source": [
    "## Plot the result\n",
    "Let's read the result from the disk and plot it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "DhVd7-3ejRdp",
    "outputId": "101d40f1-78fc-485d-eac9-70b90baf3abf"
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline  \n",
    "\n",
    "x_label = \"time step\"\n",
    "\n",
    "# voltage results\n",
    "vm_pu_file = os.path.join(output_dir, \"res_bus\", \"vm_pu.xlsx\")\n",
    "vm_pu = pd.read_excel(vm_pu_file, index_col=0)\n",
    "vm_pu.plot(label=\"vm_pu\")\n",
    "plt.xlabel(x_label)\n",
    "plt.ylabel(\"voltage mag. [p.u.]\")\n",
    "plt.title(\"Voltage Magnitude\")\n",
    "plt.grid()\n",
    "plt.show()\n",
    "\n",
    "# line loading results\n",
    "ll_file = os.path.join(output_dir, \"res_line\", \"loading_percent.xlsx\")\n",
    "line_loading = pd.read_excel(ll_file, index_col=0)\n",
    "line_loading.plot(label=\"line_loading\")\n",
    "plt.xlabel(x_label)\n",
    "plt.ylabel(\"line loading [%]\")\n",
    "plt.title(\"Line Loading\")\n",
    "plt.grid()\n",
    "plt.show()\n",
    "\n",
    "# load results\n",
    "load_file = os.path.join(output_dir, \"res_load\", \"p_mw.xlsx\")\n",
    "load = pd.read_excel(load_file, index_col=0)\n",
    "load.plot(label=\"load\")\n",
    "plt.xlabel(x_label)\n",
    "plt.ylabel(\"P [MW]\")\n",
    "plt.grid()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "time_series.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
